package org.jeecg.common.job;

import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.COSMOS;
import org.jeecg.common.cache.CacheExpires;
import org.jeecg.common.cache.DataCache;
import org.jeecg.common.cache.DistributeLock;
import org.jeecg.common.job.data.service.IJobService;
import org.jeecg.common.job.data.vo.JobInfoVo;
import org.jeecg.common.job.data.vo.JobLogVo;
import org.jeecg.common.job.enums.JobConstants;
import org.jeecg.common.util.DateUtil;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Method;
import java.util.Date;

public class SchedulerJob implements Job {
    private static Logger LG = LoggerFactory.getLogger(SchedulerJob.class);

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        long btime = 0;
        SchedulerActive.JobConfig jobConfig = null;
        try {
            // 随机休眠，保证其他节点也能执行
            Thread.sleep((int) (Math.random() * 1000));
            btime = System.currentTimeMillis();
            JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
            jobConfig = (SchedulerActive.JobConfig) jobDataMap.get("jobConfig");
            long index = jobConfig.getCount();
            if(StringUtils.isBlank(jobConfig.getNodeName())){
                return;
            }
            if(!jobConfig.getNodeName().contains(JobConstants.NODE_NAME)){
                return;
            }
            this.singleNode(jobConfig);
            if(jobConfig.isLog()) {
                LG.info("#" + index + "任务执行完成[" + jobConfig.getId() + ", " + jobConfig.getBeanName() + ", " + jobConfig.getBeanMethod() + "], 总耗时[" + ((System.currentTimeMillis() - btime) / 1000) + "s]");
            }
        } catch (IllegalStateException e) {
            LG.warn("任务执行结束[" + jobConfig.getId() + ", " + jobConfig.getBeanName() + ", " + jobConfig.getBeanMethod() + "]。");
        } catch (Exception e) {
            LG.error("任务执行失败[" + jobConfig.getId() + ", " + jobConfig.getBeanName() + ", " + jobConfig.getBeanMethod() + "]。", e);
        }
    }

    /**
     * 集群互斥节点执行
     *
     * @throws Exception
     */
    private void singleNode(SchedulerActive.JobConfig jobConfig) throws Exception {
        if(this.checkStatus(jobConfig)){
            return;
        }

        String status = "成功";
        String message = "执行成功";
        Date btime = new Date();
        try{
            Object execBean = COSMOS.getBean(jobConfig.getBeanName());
            Method method = ReflectionUtils.findMethod(execBean.getClass(), jobConfig.getBeanMethod());
            // 反射执行方法
            ReflectionUtils.invokeMethod(method, execBean);
        } catch (Exception e){
            status = "失败";
            message = e.getMessage();
            throw e;
        } finally {
            DataCache.getSingleton().delete(jobConfig.getFlagKey());
            if(!jobConfig.getJobId().startsWith("-")){
                Date etime = new Date();
                // 记录日志
                this.saveJobLog(jobConfig, btime, etime, status, message);
            }
        }
    }

    private boolean checkStatus(SchedulerActive.JobConfig jobConfig)throws Exception {
        DistributeLock lock = DataCache.getSingleton().getLock(jobConfig.getLockKey(), CacheExpires.IN_1MIN);
        try {
            lock.lock();
            String cacheKey = DataCache.getSingleton().getString(jobConfig.getCountKey());
            Long cacheCount = Long.parseLong((cacheKey == null ? "0" : cacheKey));
            LG.info("checkStatus, cacheKey:" + cacheKey + ", cacheCount:" + cacheCount + ", localCount:" + jobConfig.getCount());
            if(!cacheCount.equals(jobConfig.getCount())) {
                LG.warn("任务已经在其他节点执行，忽略本次执行。");
                jobConfig.setCount(cacheCount);
                return true;
            }

            String flagKey =  DataCache.getSingleton().getString(jobConfig.getFlagKey());
            if(StringUtils.isNotBlank(flagKey)){
                LG.warn("上次任务还未执行完成，忽略本次执行。");
                return true;
            }

            // 任务执行是否完成标记
            DataCache.getSingleton().setString(jobConfig.getFlagKey(), "1", CacheExpires.IN_1DAY);
            // 计数器缓存7天
            jobConfig.setCount(jobConfig.getCount() + 1);
            DataCache.getSingleton().setString(jobConfig.getCountKey(), jobConfig.getCount().toString(), CacheExpires.IN_ALWAYS);
        } finally {
            if (lock != null) {
                lock.unlock();
            }
        }
        return false;
    }

    private void saveJobLog(SchedulerActive.JobConfig jobConfig, Date btime, Date etime, String status, String message){
        IJobService jobService = COSMOS.getBean(IJobService.class);
        JobLogVo jobLog = new JobLogVo();
        jobLog.setJobId(jobConfig.getJobId());
        jobLog.setExecBeginTime(btime);
        jobLog.setExecEndTime(etime);
        jobLog.setExecStatus(status);
        jobLog.setJobReslut(message);
        jobLog.setServerNode(JobConstants.NODE_NAME);
        jobLog.setExecTakeTime(DateUtil.formatTime(etime.getTime() - btime.getTime()));
        jobService.saveJobLog(jobLog);

        JobInfoVo jobInfoVo = new JobInfoVo();
        jobInfoVo.setId(jobConfig.getJobId());
        jobInfoVo.setLastTime(etime);
        jobService.update(jobInfoVo, "lastTime");
    }
}
